package cn.lingyangwl.agile.food.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.model.module.tenant.TenantContext;
import cn.lingyangwl.agile.food.core.manager.GoodsAttributeValueManager;
import cn.lingyangwl.agile.food.core.mapper.GoodsAttributeMapper;
import cn.lingyangwl.agile.food.model.constants.ShopCacheKey;
import cn.lingyangwl.agile.food.model.entity.GoodsAttribute;
import cn.lingyangwl.agile.food.core.service.GoodsAttributeService;
import cn.lingyangwl.agile.tenant.core.utils.TenantUtils;
import cn.lingyangwl.framework.cache.core.service.CacheService;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 商品属性
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class GoodsAttributeServiceImpl extends ServiceImpl<GoodsAttributeMapper, GoodsAttribute> implements GoodsAttributeService {
    @Resource
    private GoodsAttributeValueManager attributeValueManager;

    @Resource
    private CacheService cacheService;

    @Override
    public boolean saveOrUpdateAttribute(GoodsAttribute req) {
        String cacheKey = ShopCacheKey.GOODS_ATTR.formatKey(TenantContext.get().getMainTenantId());
        cacheService.opsForKey().delete(cacheKey);

        LambdaQueryWrapper<GoodsAttribute> lqw = GoodsAttribute.lqw().ne(Objects.nonNull(req.getId()), GoodsAttribute::getId, req.getId())
            .eq(GoodsAttribute::getName, req.getName());
        if (this.count(lqw) > 0) {
            throw new BizException("属性名已存在");
        }
        if (Objects.nonNull(req.getId())) {
            GoodsAttribute old = this.getById(req.getId());
            if (Objects.isNull(old)) {
                throw new BizException("属性不存在");
            }

            Set<String> oldValueNameSet = old.getValues().stream().map(GoodsAttribute.Values::getName).collect(Collectors.toSet());
            Set<String> newValueNameSet = req.getValues().stream().map(GoodsAttribute.Values::getName).collect(Collectors.toSet());

            Set<String> deleted = new HashSet<>(oldValueNameSet);
            deleted.removeAll(newValueNameSet);

            if (attributeValueManager.exitAttributeValue(req.getId(), deleted)) {
                throw new BizException("属性值被其他商品占用, 不能删除属性值");
            }
        }

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 删除缓存
                cacheService.opsForKey().delete(cacheKey);
            }
        });

        return this.saveOrUpdate(req);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteAttribute(List<Long> ids) {
        String cacheKey = ShopCacheKey.GOODS_ATTR.formatKey(TenantContext.get().getMainTenantId());
        cacheService.opsForKey().delete(cacheKey);

        if (attributeValueManager.exitByAttributeId(ids)) {
            throw new BizException("该属性已被商品占用, 不能删除属性");
        }

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 删除缓存
                cacheService.opsForKey().delete(cacheKey);
            }
        });
        return this.removeBatchByIds(ids);
    }

    @Override
    public List<GoodsAttribute> listAttributeByIds(List<Long> attributeIds) {
        String cacheKey = ShopCacheKey.GOODS_ATTR.formatKey(TenantContext.get().getMainTenantId());
        if (CollUtil.isEmpty(attributeIds)) {
            return Collections.emptyList();
        }
        List<GoodsAttribute> cacheValue = cacheService.opsForHash()
            .multiGet(cacheKey, attributeIds.stream().map(String::valueOf).collect(Collectors.toList()));
        cacheValue = cacheValue.stream().filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isEmpty(cacheValue)) {
            cacheValue = TenantUtils.executeIgnore(() -> this.listByIds(attributeIds));
            if (CollUtil.isEmpty(cacheValue)) {
                return Collections.emptyList();
            }
            Map<String, GoodsAttribute> hv = cacheValue.stream()
                .collect(Collectors.toMap(e -> String.valueOf(e.getId()), Function.identity()));
            cacheService.opsForHash().putAll(cacheKey, hv);
        }

        return cacheValue;
    }
}
